Extending values on vertices to a discrete gradient vector field

During extension algorithm one has to compute lover_link for every vertex in the complex. So let us implement search for the lower link first. It requires quite a lot of code: first we find a star, then link and finally lower link for the given simplex.


In [90]:
from itertools import combinations, chain

def simplex_closure(a):    
    """Returns the generator that iterating over all subsimplices (of all dimensions) in the closure
    of the simplex a. The simplex a is also included.
    """
    return chain.from_iterable([combinations(a, l) for l in range(1, len(a) + 1)])
        
def closure(K):
    """Add all missing subsimplices to K in order to make it a simplicial complex."""
    return list({s for a in K for s in simplex_closure(a)})

def contained(a, b):
    """Returns True is a is a subsimplex of b, False otherwise."""
    return all((v in b for v in a))

def star(s, cx):
    """Return the set of all simplices in the cx that contais simplex s.
    """
    return {p for p in cx if contained(s, p)}

def intersection(s1, s2):
    """Return the intersection of s1 and s2."""
    return list(set(s1).intersection(s2))

def link(s, cx):
    """Returns link of the simplex s in the complex cx.
    """
    # Link consists of all simplices from the closed star that have 
    # empty intersection with s.
    return [c for c in closure(star(s, cx)) if not intersection(s, c)]

def simplex_value(s, f, aggregate):
    """Return the value of f on vertices of s
    aggregated by the aggregate function.
    """
    return aggregate([f[v] for v in s])

def lower_link(s, cx, f):
    """Return the lower link of the simplex s in the complex cx.
    The dictionary f is the mapping from vertices (integers)
    to the values on vertices.
    """
    sval = simplex_value(s, f, min)
    return [s for s in link(s, cx) 
            if simplex_value(s, f, max) < sval]

Let us test the above function on the simple example: full triangle with values 0, 1 and 2 on the vertices labeled with 1, 2 and 3.


In [87]:
K = closure([(1, 2, 3)])
f = {1: 0, 2: 1, 3: 2}
for v in (1, 2, 3):
    print"{0}: {1}".format((v,), lower_link((v,), K, f))


(1,): []
(2,): [(1,)]
(3,): [(1, 2), (1,), (2,)]

Now let us implement an extension algorithm. We are leaving out the cancelling step for clarity.


In [91]:
def join(a, b):
    """Return the join of 2 simplices a and b."""
    return tuple(sorted(set(a).union(b)))

def extend(K, f):
    """Extend the field to the complex K.
    Function on vertices is given in f.
    Returns the pair V, C, where V is the dictionary containing discrete gradient vector field
    and C is the list of all critical cells.
    """
    V = dict()
    C = []
    for v in (s for s in K if len(s)==1):
        # Add your own code
        pass
    return V, C

Let us test the algorithm on the example from the previous step (full triangle).


In [92]:
K = closure([(1, 2, 3)])
f = {1: 0, 2: 1, 3: 2}
extend(K, f)


Out[92]:
({(2,): (1, 2), (2, 3): (1, 2, 3), (3,): (1, 3)}, [(1,)])

In [100]:
K = closure([(1, 2, 3), (2, 3, 4)])
f = {1: 0, 2: 1, 3: 2, 4: 0}
extend(K, f)


Out[100]:
({(2,): (1, 2), (2, 3): (1, 2, 3), (3,): (1, 3)},
 [(1,), (2, 4), (2, 3, 4), (3, 4), (4,)])

In [101]:
K = closure([(1, 2, 3), (2, 3, 4)])
f = {1: 0, 2: 1, 3: 2, 4: 3}
extend(K, f)


Out[101]:
({(2,): (1, 2),
  (2, 3): (1, 2, 3),
  (3,): (1, 3),
  (3, 4): (2, 3, 4),
  (4,): (2, 4)},
 [(1,)])

In [ ]: